home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / forthcmp.zip / MULTI.4TH < prev    next >
Text File  |  1992-03-30  |  11KB  |  416 lines

  1. \ ForthCMP Multitasking Module 
  2. \ Copyright 1987 (C) By Thomas Almy.  All rights reserved.
  3.  
  4. \ Permission is granted to registered users of ForthCMP to sell or distribute
  5. \ computer programs incorporating the compiled contents of this file.
  6.  
  7. \ IBM BIOS is used for terminal I/O.
  8.  
  9. \ See the manual for usage of this module.
  10.  
  11. \   IBM is a trademark of International Business Machines, Inc.
  12.  
  13.  .( LOADING MULTI) CR
  14. INCLUDE INTS
  15. INCLUDE FARMEM1
  16. 10
  17.  
  18. DECIMAL  
  19.  
  20. 0 0 IN/OUT NEED SINGLE 
  21. 0 0 IN/OUT NEED MULTI
  22. 0 0 IN/OUT NEED PAUSE
  23. 0 0 IN/OUT NEED end-timer
  24. 0 0 IN/OUT NEED start-timer
  25.  
  26. VARIABLE ?multi         \ true if multitasking turned on
  27. VARIABLE user           \ disp into user segment--used at compile time
  28. VARIABLE CTASK          \ pointer to task list
  29. VARIABLE dispused       \ semaphore for display output
  30. VARIABLE inexpect       \ executing EXPECT -- only one at a time, please!
  31.  
  32.  \ Semaphores
  33.  
  34. 1 0 IN/OUT
  35. : SEMA  BEGIN DUP @ WHILE PAUSE REPEAT  ON ;
  36.  
  37. 1 0 IN/OUT
  38. : PHORE  OFF PAUSE ;
  39.  
  40.  
  41. 0 0 IN/OUT 
  42. : BYE  end-timer bye ;
  43.  
  44.  \ Memory management interface
  45. 1 1 IN/OUT
  46. : GET malloc IF    ." OUT OF MEMORY " BYE THEN ;
  47.  
  48.  \ USER VARIABLES 
  49. H: UALLOT  DSEG user @  +  user ! ;
  50. 1 2 IN/OUT
  51. H: UCREATE user @ CONSTANT ;
  52. H: UVARIABLE  UCREATE 2 UALLOT ;
  53. H: URESET DSEG  0 user ! ;
  54. URESET
  55.  \ redefinition of primitive I/O functions
  56. HEX
  57. 0 0 IN/OUT
  58. CODE setcursor  \ set the cursor to the correct location
  59.     CTASK [] BX MOV
  60.     CS: 12 +[BX] DH MOV     \ Y value
  61.     CS: 14 +[BX] DL MOV     \ X value
  62.     BH BH XOR
  63.     2 # AH MOV
  64.     10 INT
  65.     RET
  66. END-CODE \ setcursor
  67.  
  68. 0 0 IN/OUT
  69. CODE getcursor  \ get the correct cursor coordinates
  70.     3 # AH MOV
  71.     BH BH XOR
  72.     10 INT
  73.     CTASK [] BX MOV
  74.     DH CS: 12 +[BX] MOV     \ Y value
  75.     DL CS: 14 +[BX] MOV     \ X value
  76.     RET
  77. END-CODE \ getcursor
  78.  
  79. 2 0 IN/OUT
  80. : GOTOXY  CTASK @ 12 + CS: 2! ;
  81.  
  82. 0 2 IN/OUT
  83. : ?XY     CTASK @ 12 + CS: 2@ ;
  84.  
  85. 1 0 IN/OUT
  86. CODE emit
  87.     0E # AH MOV
  88.     BX BX XOR
  89.     10 INT
  90.     RET 
  91. END-CODE
  92.  
  93. 0 0 IN/OUT 
  94. CODE CLS
  95.     3 # AX MOV 
  96.     10 INT 
  97.     RET 
  98. END-CODE
  99.  
  100. 0 1 IN/OUT
  101. CODE ?TERMINAL 
  102.     CALL' PAUSE     \ allow another task to execute
  103.     1 # AH MOV 
  104.     16 INT 
  105.     0 # AX MOV
  106.     =0 ~ IF, AX DEC  THEN,
  107.     RET
  108. END-CODE \ ?TERMINAL
  109.  
  110. : PAD CTASK @ 16 + CS: @ ;
  111.  
  112. DECIMAL
  113.  
  114. : EMIT 
  115.     dispused SEMA 
  116.     setcursor
  117.     emit
  118.     getcursor
  119.     dispused PHORE ;
  120.  
  121. : TYPE 
  122.     dispused SEMA 
  123.     setcursor
  124.     0 ?DO COUNT emit LOOP DROP 
  125.     getcursor
  126.     dispused PHORE ;
  127.  
  128. : CS:TYPE
  129.     dispused SEMA
  130.     setcursor
  131.     0 ?DO CS: COUNT emit LOOP DROP
  132.     getcursor
  133.     dispused PHORE ;
  134.  
  135. : SPACES        \ send out all characters in a burst
  136.     dispused SEMA
  137.     setcursor
  138.     DUP 0> IF 0 DO BL emit LOOP  ELSE DROP THEN
  139.     getcursor
  140.     dispused PHORE ;
  141.  
  142.  
  143. : KEY  BEGIN ?TERMINAL setcursor UNTIL  0 8 BDOS ;
  144.  
  145.  \ EXPECT
  146.  
  147. FIND SPAN #IF DROP #ELSE VARIABLE SPAN #THEN
  148.  
  149. 0 0 IN/OUT
  150. : bu  8 emit BL emit 8 emit -1 SPAN +! ;
  151.  
  152. : EXPECT 
  153.     inexpect SEMA       \ too hard if two or more tasks want input at once!
  154.     >R SPAN OFF
  155.     BEGIN
  156.         SPAN @ R@ < WHILE       \ more room on line
  157.         KEY  dispused SEMA  setcursor  CASE
  158.         27 OF BEGIN SPAN @ 0> WHILE bu REPEAT  ENDOF
  159.         8  OF SPAN @ 0> IF bu THEN ENDOF
  160.         13 OF BL emit
  161.             R> DROP DROP 
  162.             getcursor 
  163.             dispused PHORE 
  164.             inexpect PHORE 
  165.             EXIT ENDOF
  166.         ( ELSE ) DUP emit 
  167.             OVER SPAN @ + C! 
  168.             1 SPAN +!
  169.         0 ENDCASE
  170.         getcursor dispused PHORE
  171.     REPEAT
  172.     inexpect PHORE
  173.     R> 2DROP ;
  174.  
  175.  
  176.  \ TASK CREATION 
  177. HEX
  178. H: TASK              \ values after INIT-TASKS:
  179.    CSEG FORCE CREATE HERE E92E , \ DISP 0 -- JMP ( task asleep )
  180.    DSEG CTASK @ ,  CTASK !    \     02 -- relative addr nxt task
  181.    user @ ,                   \     04 -- size of user area (not used?)
  182.    0 ,                        \     06 -- SS register contents
  183.    user @ pssize 10 * + ,     \     08 -- SP register contents
  184.    user @ pssize 10 * + rssize + , \     0A -- BP register contents
  185.    ,                          \     0C -- PC contents
  186. \ the following fields are for per-task variables
  187. \ and could be selectively elimiated if not needed if space is 
  188. \ at a premium.  In that case, offsets may need to be adjusted
  189. \ for words which use latter fields.
  190.    0 ,                        \     0E -- Message list
  191.    0 ,                        \     10 -- Timer
  192.    0 ,                        \     12 -- Y cursor coordinate
  193.    0 ,                        \     14 -- X cursor coordinate
  194.    DSEG HERE 80 ALLOT 20 + ,  \     16 -- PAD, a per-task work area
  195. 0 #IF
  196. Initially, DISP 2 has absolute address of next task. 
  197. This value as well as DISP 6 get
  198. filled in by INIT-TASKS when application is run.
  199. #THEN
  200.  
  201. CSEG FORCE  HERE  CREATE MAIN-TASK  \ Give it a name
  202. DSEG CTASK !                  \ Task list points to it
  203. 80CD ,                        \ DISP 0 -- INT 80 (task awake)
  204.    0 ,                        \     02 -- relative addr next task
  205.    0 ,                        \     04 -- NOT USED
  206.    0 ,                        \     06 -- SS register contents
  207.    0 ,                        \     08 -- SP register contents
  208.    0 ,                        \     0A -- BP register contents
  209.    0 ,                        \     0C -- PC contents
  210.    0 ,                        \     0E -- Message list
  211.    0 ,                        \     10 -- Timer
  212.    0 ,                        \     12 -- Y cursor coordinate
  213.    0 ,                        \     14 -- X cursor coordinate
  214.    DSEG HERE 80 ALLOT 20 + ,  \     16 -- PAD, a per-task work area
  215. 0 #IF
  216. DISP-2, 6, 12, and 14 get filled in by INIT-TASK.  -8 -0A and -0C
  217. are filled by first task swap (which is done by INIT-TASK).
  218. #THEN
  219.  
  220.  \ TASK INITIALIZATION
  221. 0 0 IN/OUT 
  222. : INIT-TASKS \ This MUST be executed to start multitasking
  223.     CTASK @
  224.     BEGIN ?DUP WHILE  \ for each task DO:
  225.         2+ DUP CS: @ IF  \ one follows, this isn't main task
  226.             DUP 8 + CS: @ 10 + 4 >>  GET 
  227.          OVER 4 + CS: ! \ stackseg
  228.             DUP CS: @ TUCK   \ next task
  229.         ELSE
  230.             0 SWAP CTASK @ \ next task is head of list
  231.         THEN
  232.         OVER - 2- SWAP CS: !  
  233.     REPEAT
  234.     MAIN-TASK CTASK !  
  235.     getcursor       \ sets main task cursor
  236.     ?SS: MAIN-TASK 6 + CS: !    \ sets main task stack segment
  237.     start-timer
  238.     MULTI ( GO!!! ) ;
  239.  
  240.  \ TASK DISPATCHER
  241. CODE PAUSE  
  242.     0 # ?multi [] CMP  
  243.     =0 IF, RET THEN,
  244.     CTASK [] BX MOV         \ current task
  245.     CS: 0C +[BX] POP        \ save PC
  246.     BP CS: 0A +[BX] MOV     \ save BP
  247.     SP CS: 08 +[BX] MOV     \ save SP
  248.     CS: 2 +[BX] BX ADD  
  249.     4 # BX ADD  
  250.     CLI                \ no ints during dispatch
  251.     BX JMPI  ( dispatch )
  252. END-CODE \ PAUSE
  253.  
  254. 0 #IF
  255. Tasks are linked together so that jumping to a task will cause
  256. jumping to the next if it is asleep, or doing an INT 80 if it
  257. is awake.  Thanks to Henry Laxen's Forth 83 model for the
  258. technique.
  259. #THEN
  260.  
  261. L: start-task ( the INT80 routine )  
  262.     BX POP 
  263.     BX DEC 
  264.     BX DEC                  \ Pointer to the task
  265.     CS: 6 +[BX] SS >SEG     \ restore stack segment
  266.     CS: 8 +[BX] SP MOV      \ restore SP
  267.     STI                     \ Interrupts are safe now
  268.     CS: 0A +[BX] BP MOV     \ restore BP
  269.     BX  CTASK [] MOV        \ current task
  270.     CS: 0C +[BX] JMPI       \ go!
  271. FORTH \ start-task 
  272. 0 #IF
  273. This code starts up a new task by setting up all registers,
  274. fixing CTASK, and jumping to where we left off.
  275. #THEN
  276.  
  277.  \ TASK MANAGEMENT
  278. : SINGLE  ?multi OFF ;
  279.  
  280. : MULTI   ?multi ON
  281.     ?CS: start-task 0 200  2!L  \ install interrupt vector
  282.     PAUSE  \ start with a task swap
  283. ;
  284.  
  285. 1 0 IN/OUT
  286. : WAKE 80CD CS: <- ;
  287.  
  288. 1 0 IN/OUT
  289. \ the 2e prefix byte (CS override) makes the jmp instruction 4 bytes long
  290. : SLEEP (  task -- )   E92E CS: <- ;
  291.  
  292. 1 1 IN/OUT
  293. : WAITING?  10 + CS: @ 0<> ;
  294.  
  295. 0 0 IN/OUT
  296. : STOP  CTASK @ SLEEP PAUSE ;
  297.  
  298. 0 1 IN/OUT
  299. : ACTIVE-TASKS
  300.     0 CTASK @
  301.     BEGIN
  302.         DUP WAITING? IF SWAP 1+ SWAP ELSE 
  303.             DUP CS: @ 80CD = IF SWAP 1+ SWAP THEN THEN \ check for active
  304.         DUP 2+ CS: @ + 4 + \ address of next task
  305.     DUP CTASK @ = UNTIL     \ Loop until back to start
  306.     DROP ( task address )
  307. ;
  308.  
  309.  \ MESSAGE PASSING
  310. 0 1 IN/OUT
  311. : MESSAGE?  CTASK @ 0E + CS: @ ;
  312.  
  313. 0 1 IN/OUT
  314. : GET-MESSAGE  
  315.     BEGIN MESSAGE? ?DUP 0= WHILE STOP REPEAT
  316.     DUP  0 @L CTASK @ 0E + CS: !         \ Unlink message
  317. ;   
  318.  
  319. 1 1 IN/OUT
  320. : MESSAGES
  321.     0 SWAP 0E + CS: @ ?DUP IF
  322.         BEGIN SWAP 1+ SWAP  0 @L  ?DUP 0= UNTIL
  323.     THEN ;
  324.  
  325. 2 0 IN/OUT
  326. : SEND-MESSAGE 
  327.     OVER 0 SWAP 0 !L        \ set message's next field to NIL
  328.     DUP WAITING? NOT IF DUP WAKE THEN \ fire up receiving task
  329.                                 \ unless waiting for timer
  330.     0E + DUP CS: @ ?DUP IF  \ Existing messages in queue
  331.         NIP
  332.         BEGIN DUP 0 @L ?DUP WHILE NIP REPEAT \ find end of list
  333.         0 !L  \ store message at end of list
  334.     ELSE
  335.         CS: !     \ no existing messages, put at head of queue.
  336.     THEN
  337.     PAUSE ;  \ Give it a chance to run
  338.  
  339.  \ control-break handler
  340. \ always gets control and (currently) dumps task information
  341.  
  342. 2VARIABLE cb_save
  343.  
  344. 1B CONSTANT cb_int
  345.  
  346. 0 0 IN/OUT
  347. : cbt  
  348.     CLS 
  349.     SINGLE
  350.     end-timer
  351.     ." Task statistics: "
  352.     MAIN-TASK \ start with first
  353.     BEGIN CR
  354.         HEX DUP 0 <# # # # # #> TYPE SPACE \ address
  355.         DUP WAITING? IF ." Waiting " DUP 10 + CS: @ . ." ticks" ELSE 
  356.             DUP CS: @ 80CD = IF ." Active" ELSE ." Sleeping" THEN THEN 
  357.         DUP 2+ CS: @ + 4 + \ address of next task
  358.     DUP MAIN-TASK = UNTIL     \ Loop until back to start
  359.     DROP ( task address )
  360.     bye
  361. ;
  362.  
  363.  
  364. ' cbt TASK cb-task
  365.  
  366.  
  367. L: cb_handler ( actual interrupt handler )
  368.       80CD # CS: cb-task [] MOV \ wake cb task
  369.     STI
  370.     IRET FORTH
  371.  
  372.  
  373.  \ timer
  374.  
  375. 1C CONSTANT t_int               \ timer interupt vector number
  376. CSEG FORCE 
  377. CREATE t_save 4 ALLOT           \ original interupt vector
  378. L: t_handler
  379.     PUSHF CS: t_save CALLF    \ do original functions
  380.     BX PUSH
  381.     MAIN-TASK # BX MOV ( start of list )
  382.     BEGIN,  
  383.         CS: 0 # 10 +[BX] CMP =0 ~ IF, ( non_zero time )
  384.             CS: 10 +[BX] DEC  ( count down )
  385.             =0 IF, 80CD # CS: [BX] MOV THEN, ( wake task )
  386.         THEN,
  387.         CS: 2 +[BX] BX ADD 
  388.         4 # BX ADD ( next task )
  389.         MAIN-TASK # BX CMP  
  390.     =0 UNTIL, ( back at start? )
  391.     BX POP 
  392.     IRET
  393. FORTH \ t_handler
  394.  
  395. \ timer start and end                          08:09 11/18/85
  396.  
  397. : start-timer  \ and control-break handler
  398.     t_int get-handler  t_save CS: 2!
  399.     ?CS: t_handler t_int set-handler
  400.     cb_int get-handler cb_save 2!
  401.     ?CS: cb_handler cb_int set-handler
  402. ;
  403.  
  404. : end-timer
  405.     t_save CS: 2@  t_int set-handler
  406.     cb_save 2@ cb_int set-handler
  407. ;
  408.  
  409. 2 0 IN/OUT
  410. : TIME-OUT ( ticks task -- )  DUP SLEEP 10 + CS: ! ;
  411.  
  412. : WAIT ( ticks -- ) CTASK @ TIME-OUT PAUSE ;
  413.  
  414. DSEG 0A = #IF DECIMAL #THEN
  415.